﻿using ApplicationBase.Core.Logs;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using System.Diagnostics;

namespace ApplicationBase.Core.Cache
{
    public class MemoryCacheService : ICacheService
    {
        private static Dictionary<string, Func<ICacheEntry, Task<object>>> _cacheFactories = new();

        private readonly IMemoryCache _cache;
        private readonly ILogger<MemoryCacheService> _logger;

        public MemoryCacheService(IMemoryCache cache, ILogger<MemoryCacheService> logger)
            => (_cache, _logger) = (cache, logger);

        public async Task<TItem?> GetOrCreateAsync<TItem>(string key, Func<ICacheEntry, Task<TItem>> factory)
        {
            if (!_cache.TryGetValue(key, out object? result))
            {
                var watch = Stopwatch.StartNew();
                using ICacheEntry entry = _cache.CreateEntry(key);

                result = await factory(entry).ConfigureAwait(false);
                entry.Value = result;

                watch.Stop();
                _logger.LogInformation($"<Cache>{(new CacheLog(false, key, watch.ElapsedMilliseconds, DateTime.Now, entry.SlidingExpiration)).ToJson()}</Cache>");
            }

            _logger.LogInformation($"<Cache>{(new CacheLog(true, key, 0, DateTime.Now, null)).ToJson()}</Cache>");
            return (TItem?)result;
        }

        public async Task Refresh(string key)
        {
            var factory = _cacheFactories[key];
            if (factory != null)
            {
                using ICacheEntry entry = _cache.CreateEntry(key);

                var result = await factory(entry).ConfigureAwait(false);
                entry.Value = result;
            }
        }
    }
}
